/*
 * @(#)JideBoxLayout.java
 *
 * Copyright 2002 JIDE Software Inc. All rights reserved.
 */
package com.jidesoft.swing;

import com.jidesoft.dialog.JideOptionPane;
import com.jidesoft.utils.SecurityUtils;
import com.jidesoft.utils.SystemInfo;

import javax.swing.*;
import java.awt.*;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * JideBoxLayout 在一些方面和 BoxLayout 是非常相似的, 就是所有组件的排列都是从左到右或者从上到下. 与 BoxLayout
 * 不同的是, 它有三个约束条件当添加组件到这个布局中的时候 - FIX, FLEXIBLE 和 VARY.
 * <ul>
 * <li>FIX: 使用组件首选的大小, 并且大小固定.
 * <li>FLEXIBLE: 灵活保持组件首选大小, 但是大小可以改变.
 * <li>VARY: 忽略首选大小. 它的大小由左侧区域大小计算而来.
 * </ul>
 * 这是 {@link com.jidesoft.swing.JideSplitPane} 的默认布局管理器.
 */
public class JideBoxLayout implements LayoutManager2, Serializable {

	private static final Logger LOGGER = Logger.getLogger(JideBoxLayout.class
			.getName());

	/**
	 * 当 resetToPreferredSizes 被调用时为true.
	 */
	private boolean doReset = true;

	/**
	 * 轴向, 0 为水平方向, or 1 为垂直方向.
	 */
	protected int _axis;
	protected Container _target;
	private int _gap = 0;

	protected int[] _componentSizes;

	/**
	 * 固定组件, 宽度 (或者高度, 在垂直情况下) 一直保持首选的宽度.
	 */
	public static final String FIX = "fix";

	/**
	 * 灵活的组件尽量保持首选宽度. 如果没有足够的空间, 所有灵活组件将会适当收缩.
	 */
	public static final String FLEXIBLE = "flexible";

	/**
	 * 变化组件, 宽度总是与剩下的宽度一致. 你可以允许添加多个FIX或者FLEXIBLE组件，但是只允许有一个VARY组件.
	 */
	public static final String VARY = "vary";

	private final HashMap<Component, Object> _constraintMap = new HashMap<Component, Object>();

	/**
	 * 指定组件由左到右排列.
	 */
	public static final int X_AXIS = 0;

	/**
	 * 指定组件由上到下排列.
	 */
	public static final int Y_AXIS = 1;

	/**
	 * 指定组件的排列方向为文字行的方向, 取决于目标容器的 <code>ComponentOrientation</code> 属性 .
	 */
	public static final int LINE_AXIS = 2;

	/**
	 * 指定组件的排列方向为页面行的垂向, 取决于目标容器的 <code>ComponentOrientation</code> 属性 .
	 */
	public static final int PAGE_AXIS = 3;

	private boolean _resetWhenInvalidate = true;
	private boolean _alwaysLayout = true;
	private static final long serialVersionUID = -183922972679053590L;

	/**
	 * 创建一个布局管理器按给与的轴向来排列组件.
	 *
	 * @param target
	 *            需要排列的容器
	 * @throws AWTError
	 *             如果 <code>axis</code> 值是无效的
	 */
	public JideBoxLayout(Container target) {
		this(target, X_AXIS);
	}

	/**
	 * @param target
	 *            需要排列的容器
	 * @param axis
	 *            组件的排列轴向. 可以为以下值: <code>JideBoxLayout.X_AXIS</code>,
	 *            <code>JideBoxLayout.Y_AXIS</code>,
	 *            <code>JideBoxLayout.LINE_AXIS</code> 或者
	 *            <code>JideBoxLayout.PAGE_AXIS</code>
	 */
	public JideBoxLayout(Container target, int axis) {
		this(target, axis, 0);
	}

	/**
	 * @param target
	 *            需要排列的容器
	 * @param axis
	 *            组件的排列轴向. 可以为以下值: <code>JideBoxLayout.X_AXIS</code>,
	 *            <code>JideBoxLayout.Y_AXIS</code>,
	 *            <code>JideBoxLayout.LINE_AXIS</code> 或者
	 *            <code>JideBoxLayout.PAGE_AXIS</code>
	 * @param gap
	 *            间隔
	 */
	public JideBoxLayout(Container target, int axis, int gap) {
		if (axis != X_AXIS && axis != Y_AXIS && axis != LINE_AXIS
				&& axis != PAGE_AXIS) {
			throw new AWTError("Invalid axis");
		}
		_axis = axis;
		_target = target;
		_gap = gap;
	}

	/**
	 * 排列指定的容器
	 *
	 * @param container
	 *            需要排列的容器
	 */
	public void layoutContainer(Container container) {
		synchronized (container.getTreeLock()) {
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine(this + " layoutContainer started");
			}
			Dimension containerSize = container.getSize();
			if (containerSize.height <= 0 || containerSize.width <= 0) {
				return;
			}

			Insets insets = _target.getInsets();

			if (doReset) {
				_componentSizes = new int[_target.getComponentCount()];
				int availableSize = getAvailableSize(containerSize, insets);
				availableSize -= getGapSize();
				if (availableSize <= 0) {
					return;
				}
				boolean success = calculateComponentSizes(availableSize, 0,
						_target.getComponentCount());
				if (!success) {
					if (!isAlwaysLayout()
							&& "false".equals(SecurityUtils.getProperty(
									"JideBoxLayout.alwaysLayout", "false"))) {
						for (int i = 0; i < _target.getComponentCount(); i++) {
							Component comp = _target.getComponent(i);
							setComponentToSize(comp, 0, 0, insets,
									containerSize); // set size to zero to clear
													// the layout
						}
						redoLayout(container);
					}
					return;
				}
				doReset = false;
				if (_componentSizes.length == 0) {
					container.repaint(); // repaint when the last component is
											// removed.
				}
			} else {
				int totalSize = 0;
				for (int componentSize : _componentSizes) {
					totalSize += componentSize;
				}
				boolean containerResized = totalSize + getGapSize() != getSizeForPrimaryAxis(containerSize);
				if (containerResized) {
					int availableSize = getAvailableSize(containerSize, insets);
					availableSize -= getGapSize();
					if (availableSize <= 0) {
						return;
					}
					boolean success = calculateComponentSizes(availableSize, 0,
							_target.getComponentCount());
					if (!success) {
						if (!isAlwaysLayout()
								&& "false".equals(SecurityUtils.getProperty(
										"JideBoxLayout.alwaysLayout", "false"))) {
							for (int i = 0; i < _target.getComponentCount(); i++) {
								Component comp = _target.getComponent(i);
								setComponentToSize(comp, 0, 0, insets,
										containerSize); // set size to zero to
														// clear the layout
							}
							redoLayout(container);
						}
						return;
					}
				}
			}

			ComponentOrientation o = _target.getComponentOrientation();
			boolean ltr = o.isLeftToRight();
			int location = getSizeForPrimaryAxis(insets, true);
			boolean needRedoLayout = false;
			if (!ltr && resolveAxis(_axis, o) == X_AXIS) {
				location = containerSize.width - location;
			}
			for (int i = 0; i < _target.getComponentCount(); i++) {
				Component comp = _target.getComponent(i);
				int oldSize = getPreferredSizeOfComponent(comp);
				if (!ltr && resolveAxis(_axis, o) == X_AXIS) {
					location -= _componentSizes[i];
					setComponentToSize(comp, _componentSizes[i], location,
							insets, containerSize);
					if (LOGGER.isLoggable(Level.FINE)) {
						LOGGER.fine("layoutContainer index: " + i + " size: "
								+ _componentSizes[i]);
					}
					if (_componentSizes[i] != 0)
						location -= _gap;
				} else {
					setComponentToSize(comp, _componentSizes[i], location,
							insets, containerSize);
					if (LOGGER.isLoggable(Level.FINE)) {
						LOGGER.fine("layoutContainer index: " + i + " size: "
								+ _componentSizes[i]);
					}
					location += _componentSizes[i];
					if (_componentSizes[i] != 0)
						location += _gap;
				}
				int newSize = getPreferredSizeOfComponent(comp);
				if (newSize != oldSize) {
					needRedoLayout = true;
				}
			}
			if (LOGGER.isLoggable(Level.FINE)) {
				LOGGER.fine("layoutContainer ended");
			}
			if (_target instanceof JideOptionPane) {
				for (int i = 0; i < container.getComponentCount(); i++) {
					container.getComponent(i).invalidate();
				}
				if (needRedoLayout) {
					redoLayout(container);
				}
			}
		}
	}

	private void redoLayout(Container container) {
		Component parent = container.getParent();
		while (parent != null) {
			if (parent instanceof Dialog) {
				break;
			}
			parent = parent.getParent();
		}
		if (parent != null) {
			((Dialog) parent).pack();
		}
	}

	protected boolean calculateComponentSizes(int availableSize,
			int startIndex, int endIndex) {
		int availableSizeExcludeFixed = availableSize;
		int varMinSize = 0;
		int flexMinSize = 0;
		int varIndex = -1;
		int totalFlexSize = 0;
		int totalFlexSizeMinusMin = 0;
		int lastFlexIndex = -1;
		int lastNoneZeroFlexIndex = -1;
		for (int i = startIndex; i < endIndex; i++) {
			Component comp = _target.getComponent(i);
			if (!comp.isVisible()) {
				continue;
			}
			Object constraint = _constraintMap.get(comp);
			int minimumSize = getSizeForPrimaryAxis(comp.getMinimumSize());
			int preferredSize = getSizeForPrimaryAxis(getPreferredSizeOf(comp,
					i));
			if (FIX.equals(constraint)) {
				availableSizeExcludeFixed -= Math.max(preferredSize,
						minimumSize);
			} else if (VARY.equals(constraint)) {
				varIndex = i;
				getPreferredSizeOf(comp, i); // there is a bug in jdk1.5 which
												// minimum size returns a large
												// number if preferred size is
												// not call.
				varMinSize = minimumSize;
			} else /* if (FLEXIBLE.equals(constraint)) */{
				if (preferredSize > minimumSize) {
					totalFlexSizeMinusMin += preferredSize - minimumSize;
				}
				totalFlexSize += preferredSize;
				flexMinSize += minimumSize;
				lastFlexIndex = i;
				// prevent the zero width column has a column width like 1 or 2
				// eventually
				if (preferredSize != 0) {
					lastNoneZeroFlexIndex = i;
				}
			}
		}

		if (!isAlwaysLayout()
				&& "false".equals(SecurityUtils.getProperty(
						"JideBoxLayout.alwaysLayout", "false"))
				&& availableSizeExcludeFixed - varMinSize < 0) {
			return false;
		}

		boolean hasVary = varIndex != -1;
		boolean expand = availableSizeExcludeFixed - varMinSize >= totalFlexSize;

		if (!hasVary || (hasVary && !expand)) {
			double resizeRatio;
			if (expand) {
				resizeRatio = totalFlexSize == 0 ? 0
						: (double) (availableSizeExcludeFixed - varMinSize)
								/ (double) totalFlexSize;
			} else {
				resizeRatio = totalFlexSizeMinusMin == 0 ? 0
						: (double) (availableSizeExcludeFixed - varMinSize - flexMinSize)
								/ (double) totalFlexSizeMinusMin;
			}

			for (int i = startIndex; i < endIndex; i++) {
				Component comp = _target.getComponent(i);
				if (!comp.isVisible()) {
					setComponentSize(i, 0);
				} else {
					Object constraint = _constraintMap.get(comp);
					int minimumSize = getSizeForPrimaryAxis(comp
							.getMinimumSize());
					int preferredSize = getSizeForPrimaryAxis(getPreferredSizeOf(
							comp, i));
					if (FIX.equals(constraint)) {
						setComponentSize(i,
								Math.max(preferredSize, minimumSize));
					} else if (VARY.equals(constraint)) {
						setComponentSize(i, varMinSize);
					} else /* if (FLEXIBLE.equals(constraint)) */{
						if (expand) {
							setComponentSize(i,
									(int) (preferredSize * resizeRatio));
						} else {
							setComponentSize(
									i,
									minimumSize
											+ (int) ((preferredSize - minimumSize) * resizeRatio));
						}
					}
				}
			}
		} else { // if (expand && hasVary) { // VARY component get all extra
					// spaces.
			for (int i = startIndex; i < endIndex; i++) {
				Component comp = _target.getComponent(i);
				if (!comp.isVisible()) {
					setComponentSize(i, 0);
				} else {
					Object constraint = _constraintMap.get(comp);
					int minimumSize = getSizeForPrimaryAxis(comp
							.getMinimumSize());
					int preferredSize = getSizeForPrimaryAxis(getPreferredSizeOf(
							comp, i));
					if (FIX.equals(constraint)) {
						setComponentSize(i,
								Math.max(preferredSize, minimumSize));
					} else if (VARY.equals(constraint)) {
						setComponentSize(i, availableSizeExcludeFixed
								- totalFlexSize);
					} else /* if (FLEXIBLE.equals(constraint)) */{
						setComponentSize(i,
								Math.max(preferredSize, minimumSize));
					}
				}
			}
		}

		int totalActualSize = 0;
		for (int i = startIndex; i < endIndex; i++) {
			totalActualSize += _componentSizes[i];
		}

		if (totalActualSize != availableSize) {
			if (varIndex != -1) {
				setComponentSizeByGap(varIndex, availableSize - totalActualSize);
			} else if (lastNoneZeroFlexIndex != -1) {
				setComponentSizeByGap(lastNoneZeroFlexIndex, availableSize
						- totalActualSize);
			} else if (lastFlexIndex != -1) {
				setComponentSizeByGap(lastFlexIndex, availableSize
						- totalActualSize);
			}
		}

		return true;
	}

	private void setComponentSizeByGap(int index, int gap) {
		if (SystemInfo.isJdk15Above()
				&& _target.getComponent(index).isMinimumSizeSet()) {
			setComponentSize(index, Math.max(_componentSizes[index] + gap,
					getSizeForPrimaryAxis(_target.getComponent(index)
							.getMinimumSize())));
		} else {
			setComponentSize(index, _componentSizes[index] + gap);
		}
	}

	private void setComponentSize(int index, int size) {
		if (LOGGER.isLoggable(Level.FINE)) {
			LOGGER.fine("setComponentSize index: " + index + " size: " + size);
		}
		_componentSizes[index] = size;
	}

	/**
	 * 如果布局管理器使用了组件字符, adds 添加组件 <code>comp</code> 到布局中, 与其关联的字符需要通过
	 * <code>name</code> 指定.
	 *
	 * @param name
	 *            与组件相关的字符
	 * @param component
	 *            待添加组件
	 */
	public void addLayoutComponent(String name, Component component) {
		layoutReset();
	}

	/**
	 * 返回需要容纳子组件的最小空间. 宽度是所有子组件最小宽度的和, 高度是所有子组件最小高度的最大值.
	 */
	public Dimension minimumLayoutSize(Container container) {
		int minPrimary = 0;
		int minSecondary = 0;
		Insets insets = _target.getInsets();

		synchronized (container.getTreeLock()) {
			for (int i = 0; i < _target.getComponentCount(); i++) {
				Component comp = _target.getComponent(i);
				if (!comp.isVisible()) {
					continue;
				}
				Object constraint = _constraintMap.get(comp);
				Dimension minimumSize = comp.getMinimumSize();
				if (FIX.equals(constraint)) {
					minPrimary += getPreferredSizeOfComponent(comp);
				} else {
					minPrimary += getSizeForPrimaryAxis(minimumSize);
				}
				int secSize = getSizeForSecondaryAxis(minimumSize);
				if (secSize > minSecondary)
					minSecondary = secSize;
			}

			if (insets != null) {
				minPrimary += getSizeForPrimaryAxis(insets, true)
						+ getSizeForPrimaryAxis(insets, false);
				minSecondary += getSizeForSecondaryAxis(insets, true)
						+ getSizeForSecondaryAxis(insets, false);
			}
		}

		ComponentOrientation o = _target.getComponentOrientation();
		if (resolveAxis(_axis, o) == X_AXIS) {
			return new Dimension(minPrimary + getGapSize(), minSecondary);
		} else {
			return new Dimension(minSecondary, minPrimary + getGapSize());
		}
	}

	/**
	 * 返回需要容纳子组件的首选最小空间. 宽度是所有子组件首选最小宽度的和, 高度是所有子组件首选最小高度的最大值.
	 */
	public Dimension preferredLayoutSize(Container container) {
		int prePrimary = 0;
		int preSecondary = 0;
		Insets insets = _target.getInsets();

		synchronized (container.getTreeLock()) {
			for (int i = 0; i < _target.getComponentCount(); i++) {
				Component comp = _target.getComponent(i);
				if (!comp.isVisible()) {
					continue;
				}
				Dimension preferredSize = getPreferredSizeOf(comp, i);
				prePrimary += getSizeForPrimaryAxis(preferredSize);
				int secSize = getSizeForSecondaryAxis(preferredSize);
				if (secSize > preSecondary)
					preSecondary = secSize;
			}

			if (insets != null) {
				prePrimary += getSizeForPrimaryAxis(insets, true)
						+ getSizeForPrimaryAxis(insets, false);
				preSecondary += getSizeForSecondaryAxis(insets, true)
						+ getSizeForSecondaryAxis(insets, false);
			}
		}
		ComponentOrientation o = _target.getComponentOrientation();
		if (resolveAxis(_axis, o) == X_AXIS) {
			return new Dimension(prePrimary + getGapSize(), preSecondary);
		} else {
			return new Dimension(preSecondary, prePrimary + getGapSize());
		}
	}

	private int getGapSize() {
		if (_gap == 0) {
			return 0;
		} else {
			int count = 0;
			for (int i = 0; i < _target.getComponentCount(); i++) {
				if (_target.getComponent(i).isVisible()) {
					count++;
				}
			}
			return Math.max(0, (count - 1)) * _gap;
		}
	}

	/**
	 * 从布局中移除指定的组件.
	 *
	 * @param comp
	 *            需要移除的组件
	 */
	public void removeLayoutComponent(Component comp) {
		_constraintMap.remove(comp);

		if (comp instanceof JideSplitPaneDivider)
			layoutReset();
	}

	//
	// LayoutManager2
	//

	/**
	 * 添加指定的组件到布局，使用指定的约束物体.
	 *
	 * @param comp
	 *            待添加组件
	 * @param constraints
	 *            组件添加到布局的位置或者方式.
	 */
	public void addLayoutComponent(Component comp, Object constraints) {
		if (constraints == null)
			_constraintMap.put(comp, FLEXIBLE);
		else
			_constraintMap.put(comp, constraints);
		layoutReset();
	}

	private void layoutReset() {
		doReset = true;
		if (LOGGER.isLoggable(Level.FINE)) {
			LOGGER.fine(this + " layoutReset");
		}
	}

	/**
	 * 返回想 x 轴的准线. 指定组件相对其他组件的对其方式. 这个值需要是一个 0 和 1 之间的数值, 0 代表与原点对齐, 1
	 * 代表与原点最远点对齐, 0.5 代表中心, 等等.
	 */
	public synchronized float getLayoutAlignmentX(Container target) {
		return 0.0f;
	}

	/**
	 * 返回想 y 轴的准线. 指定组件相对其他组件的对其方式. 这个值需要是一个 0 和 1 之间的数值, 0 代表与原点对齐, 1
	 * 代表与原点最远点对齐, 0.5 代表中心, 等等.
	 */
	public synchronized float getLayoutAlignmentY(Container target) {
		return 0.0f;
	}

	/**
	 * 使布局无效, 注意如果布局管理器有缓存信息它将会被丢弃.
	 */
	public synchronized void invalidateLayout(Container c) {
		if (isResetWhenInvalidate() || componentCountChanged(c)) {
			layoutReset();
		}
	}

	protected boolean componentCountChanged(Container c) {
		if (_componentSizes == null) {
			return true;
		}
		int oldLength = 0;
		for (int _componentSize : _componentSizes) {
			if (_componentSize > 0) {
				oldLength++;
			}
		}
		int newLength = 0;
		for (int i = 0; i < c.getComponentCount(); i++) {
			if (c.getComponent(i).isVisible()) {
				newLength++;
			}
		}
		return newLength != oldLength;
	}

	/**
	 * 返回布局的最大值, 两个方向的 Integer.MAX_VALUE.
	 */
	public Dimension maximumLayoutSize(Container target) {
		return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
	}

	/**
	 * 返回组件首选大小的伸展宽度.
	 *
	 * @param c
	 *            组件
	 * @return 组件的首先大小.
	 */
	protected int getPreferredSizeOfComponent(Component c) {
		return getSizeForPrimaryAxis(c.getPreferredSize());
	}

	/**
	 * 返回组件最小空间的伸展宽度.
	 *
	 * @param c
	 *            组件
	 * @return 组件最小空间.
	 */
	int getMinimumSizeOfComponent(Component c) {
		return getSizeForPrimaryAxis(c.getMinimumSize());
	}

	/**
	 * 返回组件伸展宽度.
	 *
	 * @param c
	 *            组件
	 * @return 组件的大小.
	 */
	protected int getSizeOfComponent(Component c) {
		return getSizeForPrimaryAxis(c.getSize());
	}

	/**
	 * 返回有效的宽度, 基于容器的大小和 Insets.
	 *
	 * @param containerSize
	 *            容器的大小
	 * @param insets
	 *            边界区域
	 * @return 可用大小.
	 */
	protected int getAvailableSize(Dimension containerSize, Insets insets) {
		if (insets == null)
			return getSizeForPrimaryAxis(containerSize);
		return (getSizeForPrimaryAxis(containerSize) - (getSizeForPrimaryAxis(
				insets, true) + getSizeForPrimaryAxis(insets, false)));
	}

	/**
	 * 返回左边边界区域, 如果边界区域是 null 返回 0.
	 *
	 * @param insets
	 *            边界区域
	 * @return 初始位置.
	 */
	protected int getInitialLocation(Insets insets) {
		if (insets != null)
			return getSizeForPrimaryAxis(insets, true);
		return 0;
	}

	/**
	 * 设置组件 c 的宽度为 size, 放置到 x 轴的 location, y 轴的 insets . containersize
	 * 的高度和宽度.高度要少于顶部和底部的边界区域.
	 *
	 * @param c
	 *            组件
	 * @param size
	 *            组件的大小
	 * @param location
	 *            组件的位置
	 * @param insets
	 *            组件的边界区域
	 * @param containerSize
	 *            容器的大小
	 */
	protected void setComponentToSize(Component c, int size, int location,
			Insets insets, Dimension containerSize) {
		if (insets != null) {
			ComponentOrientation o = _target.getComponentOrientation();
			if (resolveAxis(_axis, o) == X_AXIS) {
				c.setBounds(
						location,
						Math.max(insets.top, 0),
						Math.max(size, 0),
						Math.max(containerSize.height
								- (insets.top + insets.bottom), 0));
			} else {
				c.setBounds(
						Math.max(insets.left, 0),
						location,
						Math.max(containerSize.width
								- (insets.left + insets.right), 0),
						Math.max(size, 0));
			}
		} else {
			ComponentOrientation o = _target.getComponentOrientation();
			if (resolveAxis(_axis, o) == X_AXIS) {
				c.setBounds(location, 0, Math.max(size, 0),
						Math.max(containerSize.height, 0));
			} else {
				c.setBounds(0, location, Math.max(containerSize.width, 0),
						Math.max(size, 0));
			}
		}
	}

	/*
	 * If the axis == JideBoxLayout.X_AXIS, the width is returned, otherwise the
	 * height.
	 */
	int getSizeForPrimaryAxis(Dimension size) {
		ComponentOrientation o = _target.getComponentOrientation();
		if (resolveAxis(_axis, o) == X_AXIS) {
			return size.width;
		} else {
			return size.height;
		}
	}

	/*
	 * If the axis == X_AXIS, the height is returned, otherwise the width.
	 */
	int getSizeForSecondaryAxis(Dimension size) {
		ComponentOrientation o = _target.getComponentOrientation();
		if (resolveAxis(_axis, o) == X_AXIS) {
			return size.height;
		} else {
			return size.width;
		}
	}

	/*
	 * Returns a particular value of the inset identified by the axis and
	 * <code>isTop</code><p>. axis isTop 0 true - left 0 false - right 1 true -
	 * top 1 false - bottom
	 */
	int getSizeForPrimaryAxis(Insets insets, boolean isTop) {
		ComponentOrientation o = _target.getComponentOrientation();
		if (resolveAxis(_axis, o) == X_AXIS) {
			if (isTop) {
				return insets.left;
			} else {
				return insets.right;
			}
		} else {
			if (isTop) {
				return insets.top;
			} else {
				return insets.bottom;
			}
		}
	}

	/*
	 * Returns a particular value of the inset identified by the axis and
	 * <code>isTop</code><p>. axis isTop 0 true - left 0 false - right 1 true -
	 * top 1 false - bottom
	 */
	int getSizeForSecondaryAxis(Insets insets, boolean isTop) {
		ComponentOrientation o = _target.getComponentOrientation();
		if (resolveAxis(_axis, o) == X_AXIS) {
			if (isTop) {
				return insets.top;
			} else {
				return insets.bottom;
			}
		} else {
			if (isTop) {
				return insets.left;
			} else {
				return insets.right;
			}
		}
	}

	/**
	 * 获取组件约束的集合.
	 *
	 * @return 约束集合
	 */
	public Map<Component, Object> getConstraintMap() {
		return _constraintMap;
	}

	/**
	 * 给定 4 个轴向值中的一个, 把他解析到绝对轴向. 相对轴向值 PAGE_AXIS 和 LINE_AXIS 转为他们给定的相对物的
	 * ComponentOrientation 值. 绝对轴 X_AXIS 和 Y_AXIS 恢复到未修改状态.
	 *
	 * @param axis
	 *            待解析的轴向
	 * @param o
	 *            待解析的 ComponentOrientation
	 * @return 解析后的轴向
	 */
	protected static int resolveAxis(int axis, ComponentOrientation o) {
		int absoluteAxis;
		if (axis == LINE_AXIS) {
			absoluteAxis = o.isHorizontal() ? X_AXIS : Y_AXIS;
		} else if (axis == PAGE_AXIS) {
			absoluteAxis = o.isHorizontal() ? Y_AXIS : X_AXIS;
		} else {
			absoluteAxis = axis;
		}
		return absoluteAxis;
	}

	/**
	 * 获取每个组件间的间隔.
	 *
	 * @return 每个组件的间隔.
	 */
	public int getGap() {
		return _gap;
	}

	/**
	 * 设置每个组件间的间隔. 确保在你改变间隔之后调用 doLayout() .
	 *
	 * @param gap
	 *            间隔
	 */
	public void setGap(int gap) {
		_gap = gap;
	}

	protected Dimension getPreferredSizeOf(Component comp, int atIndex) {
		// for JTextArea, the preferred size only returns the height of one line
		// if the size is never set.
		if (comp instanceof JTextArea
				&& getSizeForSecondaryAxis(comp.getSize()) == 0) {
			Insets insets = _target.getInsets();
			ComponentOrientation o = _target.getComponentOrientation();
			Dimension size = _target.getSize();
			if (resolveAxis(_axis, o) == X_AXIS) {
				comp.setSize(new Dimension(comp.getPreferredSize().width,
						size.height - insets.top - insets.bottom));
			} else {
				comp.setSize(new Dimension(size.width - insets.left
						- insets.right, comp.getPreferredSize().height));
			}
		}
		Dimension preferredSize = comp.getPreferredSize();
		Dimension minimumSize = comp.getMinimumSize();
		if (preferredSize.height < minimumSize.height) {
			preferredSize.height = minimumSize.height;
		}
		if (preferredSize.width < minimumSize.width) {
			preferredSize.width = minimumSize.width;
		}
		Dimension maximumSize = comp.getMaximumSize();
		if (preferredSize.height > maximumSize.height
				&& maximumSize.height != 32767) { // 32767 is hard-coded inside
													// Swing. If you set maximum
													// size to null, the maximum
													// size is 32767.
			preferredSize.height = maximumSize.height;
		}
		if (preferredSize.width > maximumSize.width
				&& maximumSize.height != 32767) {
			preferredSize.width = maximumSize.width;
		}
		return preferredSize;
	}

	/**
	 * 检查布局是否会重置当 {@link #invalidateLayout(java.awt.Container)} 被调用后.
	 *
	 * @return true or false.
	 */
	public boolean isResetWhenInvalidate() {
		return _resetWhenInvalidate;
	}

	/**
	 * 设置标记 , 如果 {@link #invalidateLayout(java.awt.Container)} 被调用后布局是否重置.
	 *
	 * @param resetWhenInvalidate
	 *            标记
	 */
	public void setResetWhenInvalidate(boolean resetWhenInvalidate) {
		_resetWhenInvalidate = resetWhenInvalidate;
	}

	/**
	 * 获取轴向.
	 *
	 * @return 轴向.
	 */
	public int getAxis() {
		return _axis;
	}

	/**
	 * 设置轴向. 改变 axis 后, 你需要在拥有这个布局的容器上调用 doLayout 方法.
	 *
	 * @param axis
	 *            新的轴向.
	 */
	public void setAxis(int axis) {
		_axis = axis;
	}

	/**
	 * 检查 alwaysLayout 标记是否为 true. 如果true, 布局管理器会一直布局组件,
	 * 即使没有足够的空间满足所有固定组件的最小空间需求.默认为true.
	 *
	 * @return true 或者 false.
	 */
	public boolean isAlwaysLayout() {
		return _alwaysLayout;
	}

	/**
	 * 设置 alwaysLayout 标记.
	 *
	 * @param alwaysLayout
	 *            true时会一直布局组件, 即使没有足够的空间满足所有固定组件的最小空间需求.
	 */
	public void setAlwaysLayout(boolean alwaysLayout) {
		_alwaysLayout = alwaysLayout;
	}
}
